<?php
	/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
	 *
	 *	WhyWDB (World of Warcraft cache parser.)
	 *	Copyright (C) 2009, Brent Flap <blog.cyberdevil.nl>
	 *	
	 *	WhyWDB is free software: you can redistribute it and/or modify
	 *	it under the terms of the GNU General Public License as published by
	 *	the Free Software Foundation, either version 3 of the License, or
	 *	any later version.
 	 *
 	 *	This program is distributed in the hope that it will be useful,
	 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
	 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	 *	GNU General Public License for more details.
	 *
	 *	You should have received a copy of the GNU General Public License
	 *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	 *
	 *	Requirements:	php-cli
	 *	Usage example:	php whywdb.php filename.wdb
	 *	Version:	1.0
	 *	Build:		2 (Dec. 8 -2009)
	 *
	 *	Notes: Only itemcache.wdb, gameobjectcache.wdb and creaturecache.wdb are fully working, others are still in development.
	 *
	 *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
	ini_set("memory_limit","280M");
	require_once('whywdb_wdb_structs.php');
	require_once('whywdb_sql_structs.php');
	class whywdb{
		var $signature;
		var $build;
		var $locale;
		var $unk1;
		var $unk2;
		var $unk3;
		
		function Siganture($sig){
			// Below we have listed the signature, source: wowwiki.com
			$signature = array(
				"BDIW"	=> "Itemcache",
				"BDNW"	=> "Itemnamecache",
				"BOGW"	=> "Gameobjectcache",
				"BOMW"	=> "Creaturecache",
				"CPNW"	=> "Npccache",
				"NDRW"	=> "Wowcache",
				"TSQW"	=> "Questcache",
				"XTIW"	=> "Itemtextcache",
				"XTPW"	=> "Pagetextcache",
			);
			return $signature[$sig];
		}
		function createSQL($data,$sig,$method){
			echo "Creating query.\n";
			global $sql_structs;
			$query;
			$count[0] = 0;
			if($method == "REPLACE" || $method == "INSERT"){
				foreach($sql_structs[$sig] as $key => $value){
					if($count[0] < 1){
						$query = $method." INTO `".$value."` (";
					}
					else if($count[0] == "1"){
						$query .= "`entry`,`".$key."`";
					}
					else{
						$query .= ",`".$key."`";
					}
					$count[0]++;
				}
				$query .= ") VALUES ";
				$count[1] = 0;
				foreach($data as $k => $v){
					$count[2] = 0;
					if($count[1] < 1){
						$query .= "\n(";
					}
					else{
						$query .= "\n,(";
					}
					foreach($sql_structs[$sig] as $key => $value){
						if($count[2] < 1){
							$query .= "\"".$k."\"";
						}
						else if($count[2] > 0){
							$query .= ",\"".$v[$value]."\"";
						}
						$count[2]++;
					}
					$count[1]++;
					$query .= "')";
				}
				$query .= ";";
			}
			else if($method == "UPDATE"){
				foreach($data as $k => $v){
					$count[0] = 0;
					foreach($sql_structs[$sig] as $key => $value){
						if($count[0] == 0){
							$query .= "UPDATE `".$value."` SET ";
						}
						else if($count[0] == 1){
							$query .= "`".$key."`=\"".$v[$value]."\"";
						}
						else{
							$query .= ", `".$key."`=\"".$v[$value]."\"";
						}
						$count[0]++;
					}
					$query .= ";\n";
				}
			}
			echo "Done with creating query.\n";
			return $query;
		}
		function Structs($sig){
			global $wdb_structs;
			switch($sig){
				case "BDIW": return $wdb_structs['BDIW']; break; // itemcache
				case "TSQW": return $wdb_structs['TSQW']; break; // questcache
				case "BOMW": return $wdb_structs['BOMW']; break; // creaturecache
				case "BOGW": return $wdb_structs['BOGW']; break; // gameobjectcache
				
				default: die("Signature not found.");
			}
		}
		function reverseHex($input){
			$split = str_split($input,2);
			krsort($split);
			$output;
			foreach($split as $s){
				$output .= $s;
			}
			return $output;
		}
		function Parse($filename,$sqlMethod){
			$handle = fopen($filename, "r");
			
			$this->signature = fread($handle, 4);
			$this->build = hexdec($this->reverseHex(bin2hex(fread($handle, 4)))); // FFFFFFFF
			$this->locale = fread($handle, 4);
			$this->locale = substr($this->locale,-1).substr($this->locale,-2,1).substr($this->locale,-3,1).substr($this->locale,-4,1);
			
			$this->unk1 = fread($handle, 4);
			$this->unk2 = fread($handle, 4);
			$this->unk3 = fread($handle, 4);
			
			$item = array();
			$count = 0;
			$debug = 0;
			
			while (!feof($handle)) {
				// Item Entry
				$id[0] = bin2hex(fread($handle, 1));
				$id[1] = bin2hex(fread($handle, 1));
				$entry = hexdec($id[1].$id[0]);
				
				bin2hex(fread($handle, 2));
				
				// Item Lenght
				$lenght[0] = bin2hex(fread($handle, 1));
				$lenght[1] = bin2hex(fread($handle, 1));
				$lenght[2] = hexdec($lenght[1].$lenght[0]);
				
				if($entry != "0"){
					$item[$entry] = fread($handle, $lenght[2]+2);
				}
				
				$struct = $this->Structs($this->signature);
			}
			fclose($handle);
			$count = 0;
			$data = array();
			foreach($item as $entry => $item_v){
				echo "Count: ".$count."\tEntry: ".$entry."\n";
				foreach($struct as $k => $v){
					$value = explode(",",$v);
					$item_v = substr($item_v,$value[0],strlen($item_v));
					if($value[1] != "string" && $value[1] != "float"){
						if($value[1] < 2){
							$data[$entry][$k] = hexdec(bin2hex(substr($item_v,0,$value[1])));
							$item_v = substr($item_v,$value[1],strlen($item_v));
						}
						else if($value[1] > 1){
							$data[$entry][$k] = (int)hexdec($this->reverseHex(bin2hex(substr($item_v,0,$value[1]))));
							if($data[$entry][$k]=="0"){
								$item_v = substr($item_v,$value[1]-$value[2],strlen($item_v));
							}
							else{
								$item_v = substr($item_v,$value[1],strlen($item_v));
							}
						}
					}
					else if($value[1] == "float"){
						list(,$float) = unpack("f*", substr($item_v,0,4));
						$data[$entry][$k] = round($float,4);
						if($data[$entry][$k]=="0"){
							$item_v = substr($item_v,4-$value[2],strlen($item_v));
						}
						else{
							$item_v = substr($item_v,4,strlen($item_v));
						}
					}
					else if($value[1] == "string"){
						$string;
						$loop = true;
						while($loop){
							$input = substr($item_v,0,1);
							$item_v = substr($item_v,1,strlen($item_v));
							if(bin2hex($input)=="00"){
								//$item_v = substr($item_v,3,strlen($item_v));
								$loop = false;
							}
							$string .= $input;
						}
						$string = substr($string,0,strlen($string)-1);
						$string = str_replace("'","\'",$string);
						$string = str_replace("\"","\\\"",$string);
						$data[$entry][$k] = $string;
					}
					unset($string);
					//echo strlen($item_v)."\t".$k."\n"; // Debugging
				}
				$count++;
			}

			echo "Writing data to file.\n";
			$filename = $this->Siganture($this->signature).".sql";
			$handle = fopen($filename, 'a') or die("can't open file");
			fwrite($handle, $this->createSQL($data,$this->signature,$sqlMethod));
			fclose($handle);
			echo "Done with writing data to file.\n";
			
			//Debugging
			/*foreach($data as $data_k => $data_v){
				if($data_k == "3703"){
					foreach($data_v as $k => $v){
						echo $data_k."\t".$k."\t".$v."\n";
					}
					echo "\n";
				}
			}*/
		}
	}
	$args = array('f' => $_SERVER["argv"][1],'m' => $_SERVER["argv"][2]);
	if(strtoupper($args['m']) != "UPDATE" && strtoupper($args['m']) != "INSERT" && strtoupper($args['m']) != "REPLACE" || !isset($args['f'])){
		echo "Useage: php whywdb.php filename.wdb querymethod\n";
		echo "Notes: Only itemcache.wdb, gameobjectcache.wdb and creaturecache.wdb are fully working, others are still in development.\n";
		echo "Notes: Query methods you can choose off are: update, replace and insert\n";
		echo "Example: php whywdb.php itemcache.wdb replace";
	}
	else{
		$whywdb = new whywdb();
		$whywdb->Parse($args['f'],strtoupper($args['m']));
	}
?>